﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.IO;

namespace comm.objst
{
    #region RImage
    [Serializable]
    public class RImage : RPack.Resource
    {
        [Serializable]
        public class Frame
        {
            public int X { get; set; }
            public int Y { get; set; }
            public int Width { get; set; }
            public int Height { get; set; }
            public int CentreX { get; set; }
            public int CentreY { get; set; }
        }

        public int Width { get; set; }
        public int Height { get; set; }
        public byte[] ImageBytes { get; set; }

        private List<Frame> _frameList = null;
        public List<Frame> FrameList
        {
            get
            {
                if (_frameList == null)
                {
                    _frameList = new List<Frame>();
                }
                return _frameList;
            }
            set { _frameList = value; }
        }

        public Frame GetFrame(int frameIndex)
        {
            if (frameIndex > -1 && frameIndex < _frameList.Count)
            {
                return _frameList[frameIndex];
            }
            return null;
        }
    }
    #endregion

    #region RKeyFrameAnim
    [Serializable]
    public class RKeyFrameAnim : RPack.Resource
    {
        [Serializable]
        public class Image
        {
            private string _name = null;
            public string Name
            {
                get
                {
                    if (_name == null)
                    {
                        _name = "";
                    }
                    return _name;
                }
                set { _name = value; }
            }

            private string _resKey = null;
            public string ResKey
            {
                get
                {
                    if (_resKey == null)
                    {
                        _resKey = "";
                    }
                    return _resKey;
                }
                set { _resKey = value; }
            }

            private List<int> _frameList = null;
            public List<int> FrameList
            {
                get
                {
                    if (_frameList == null)
                    {
                        _frameList = new List<int>();
                    }
                    return _frameList;
                }
                set { _frameList = value; }
            }
        }

        private Dictionary<string, Image> _imageDic = null;
        public Dictionary<string, Image> ImageDic
        {
            get
            {
                if (_imageDic == null)
                {
                    _imageDic = new Dictionary<string, Image>();
                }
                return _imageDic;
            }
            set { _imageDic = value; }
        }

        private List<uint> _timeList = null;
        public List<uint> TimeList
        {
            get
            {
                if (_timeList == null)
                {
                    _timeList = new List<uint>();
                }
                return _timeList;
            }
            set { _timeList = value; }
        }

        public int GetMaxImageFrameCount()
        {
            var max = 0;
            foreach (var image in _imageDic.Values)
            {
                max = Math.Max(image.FrameList.Count, max);
            }
            return max;
        }
        public void TrimTimeList()
        {
            var max = GetMaxImageFrameCount();
            if (max != _timeList.Count)
            {
                var timeList = new List<uint>();
                for (int i = 0; i < max; i++)
                {
                    timeList.Add(_timeList.Count > i ? _timeList[i] : 100);
                }
                _timeList.Clear();
                _timeList.AddRange(timeList);
            }
        }
        public uint GetTotalTime()
        {
            uint total = 0;
            foreach (var time in _timeList)
            {
                total += time;
            }
            return total;
        }
    }
    #endregion

    //#region RSprite
    [Serializable]
    public class RSprite : RPack.Resource, IActionStep
    {
        #region Element,ILayer
        public interface ILayer
        {
            MapLayerType MapLayerType { get; set; }
            int OffsetX { get; set; }
            int OffsetY { get; set; }
            int ZIndex { get; set; }
        }

        [Serializable]
        public abstract class Element
        {
            string _name = null;
            public string Name
            {
                get
                {
                    if (_name == null)
                    {
                        _name = "";
                    }
                    return _name;
                }
                set { _name = value; }
            }

            public Element Clone()
            {
                return comm.BinarySerializer.Deserialize<Element>(comm.BinarySerializer.Serialize(this));
            }
            public abstract Element CopyToGroupElement(Element el);
        }
        #endregion

        #region Label
        [Serializable]
        public class Point : Element, ILayer
        {
            public MapLayerType MapLayerType { get; set; }
            public int OffsetX { get; set; }
            public int OffsetY { get; set; }
            public int ZIndex { get; set; }

            public override Element CopyToGroupElement(Element el)
            {
                var point = (Point)el.Clone();
                point.Name = Name;
                point.MapLayerType = MapLayerType;
                point.ZIndex = ZIndex;
                return point;
            }
        }

        [Serializable]
        public class Label : Element
        {
            public int DesignX { get; set; }
            public int DesignY { get; set; }

            Dictionary<string, Point> _pointDic = null;
            public Dictionary<string, Point> PointDic
            {
                get
                {
                    if (_pointDic == null)
                    {
                        _pointDic = new Dictionary<string, Point>();
                    }
                    return _pointDic;
                }
                set { _pointDic = value; }
            }

            public override Element CopyToGroupElement(Element el)
            {
                var label = (Label)el.Clone();
                label.Name = Name;
                label.DesignX = DesignX;
                label.DesignY = DesignY;
                var newDic = new Dictionary<string, Point>();
                foreach (var pointKey in PointDic.Keys)
                {
                    if (!label.PointDic.ContainsKey(pointKey))
                    {
                        newDic.Add(pointKey, (Point)PointDic[pointKey].Clone());
                    }
                    else
                    {
                        newDic.Add(pointKey, (Point)PointDic[pointKey].CopyToGroupElement(label.PointDic[pointKey]));
                    }
                }
                label.PointDic = newDic;
                return label;
            }
        }
        #endregion

        #region Path
        [Serializable]
        public class Path : Element, ILayer
        {
            public static List<int[]> MergeAllPath(Path[] paths)
            {
                var list = new List<int[]>();
                var cList = new List<string>();
                foreach (var path in paths)
                {
                    foreach (var dot in path.PathList)
                    {
                        var x = dot[0] + path.OffsetX;
                        var y = dot[1] + path.OffsetY;
                        var key = x + "," + y;
                        if (!cList.Contains(key))
                        {
                            list.Add(new int[] { x, y });
                            cList.Add(key);
                        }
                    }
                }
                return list;
            }

            public MapLayerType MapLayerType { get { return objst.MapLayerType.PATH; } set { } }
            public int OffsetX { get; set; }
            public int OffsetY { get; set; }
            public int ZIndex { get; set; }

            List<int[]> _pathList = null;
            public List<int[]> PathList
            {
                get
                {
                    if (_pathList == null)
                    {
                        _pathList = new List<int[]>();
                    }
                    return _pathList;
                }
                set { _pathList = value; }
            }

            public override Element CopyToGroupElement(Element el)
            {
                var path = (Path)el.Clone();
                path.Name = Name;
                path.MapLayerType = MapLayerType;
                path.ZIndex = ZIndex;
                return path;
            }
        }
        #endregion

        #region StartType
        [Serializable]
        public enum StartType
        {
            ZERO = 0,
            END = 1
        }
        #endregion

        #region Timeline
        [Serializable]
        public abstract class Timeline : Element
        {
            string _resKey = null;
            public string ResKey
            {
                get
                {
                    if (_resKey == null)
                    {
                        _resKey = "";
                    }
                    return _resKey;
                }
                set { _resKey = value; }
            }

            public StartType StartType { get; set; }
            public uint? MaxLoopCount { get; set; }
            public uint BeginTime { get; set; }
            public uint? EndTime { get; set; }

            public uint GetResTotalTime(RPack rpack)
            {
                uint count = 0;
                if (this is KeyFrameAnim)
                {
                    var res = rpack.GetResource<comm.objst.RKeyFrameAnim>(ResKey);
                    if (res != null)
                    {
                        count = res.GetTotalTime();
                    }
                }
                else if (this is Image)
                {
                    count = 1;
                }
                return count;
            }
            public uint[] GetResTimes(RPack rpack)
            {
                var list = new List<uint>();
                if (this is KeyFrameAnim)
                {
                    var res = rpack.GetResource<comm.objst.RKeyFrameAnim>(ResKey);
                    if (res != null)
                    {
                        list.AddRange(res.TimeList);
                    }
                }
                else if (this is Image)
                {
                    list.Add(1);
                }
                return list.ToArray();
            }
            public uint GetEndTime(RPack rpack)
            {
                if (MaxLoopCount == 0 || BeginTime > EndTime)
                {
                    return 0;
                }
                uint resTotalTime = GetResTotalTime(rpack);
                if (MaxLoopCount != null)
                {
                    var total = resTotalTime * MaxLoopCount.Value;
                    if (EndTime == null || total + BeginTime < EndTime.Value)
                    {
                        return total + BeginTime;
                    }
                }
                if (EndTime == null)
                {
                    return resTotalTime + BeginTime;
                }
                return EndTime.Value;
            }
            public uint GetTotalTime(RPack rpack)
            {
                var end = GetEndTime(rpack);
                if (end != 0)
                {
                    return end - BeginTime;
                }
                return 0;
            }
        }
        #endregion

        #region Image
        [Serializable]
        public class Image : Timeline, ILayer
        {
            public MapLayerType MapLayerType { get; set; }
            public int OffsetX { get { return 0; } set { } }
            public int OffsetY { get { return 0; } set { } }
            public int ZIndex { get; set; }
            public int FrameIndex { get; set; }

            public override Element CopyToGroupElement(Element el)
            {
                var image = (Image)el.Clone();
                image.Name = Name;
                image.MapLayerType = MapLayerType;
                image.ZIndex = ZIndex;
                image.ResKey = ResKey;
                image.StartType = StartType;
                image.BeginTime = BeginTime;
                image.EndTime = EndTime;
                image.MaxLoopCount = MaxLoopCount;
                return image;
            }
        }
        #endregion

        #region KeyFrameAnim
        [Serializable]
        public class KeyFrameAnim : Timeline, ILayer
        {
            public MapLayerType MapLayerType { get; set; }
            public int OffsetX { get { return 0; } set { } }
            public int OffsetY { get { return 0; } set { } }
            public int ZIndex { get; set; }

            string _imageKey = null;
            public string ImageKey
            {
                get
                {
                    if (_imageKey == null)
                    {
                        _imageKey = "";
                    }
                    return _imageKey;
                }
                set { _imageKey = value; }
            }

            public override Element CopyToGroupElement(Element el)
            {
                var kfanim = (KeyFrameAnim)el.Clone();
                kfanim.Name = Name;
                kfanim.MapLayerType = MapLayerType;
                kfanim.ZIndex = ZIndex;
                kfanim.ResKey = ResKey;
                kfanim.StartType = StartType;
                kfanim.BeginTime = BeginTime;
                kfanim.EndTime = EndTime;
                kfanim.MaxLoopCount = MaxLoopCount;
                return kfanim;
            }
        }
        #endregion

        #region Group
        [Serializable]
        public class Group : Element
        {
            public static List<int[]> MergeAllGroupPath(Group[] groups)
            {
                var list = new List<Path>();
                foreach (var group in groups)
                {
                    foreach (var item in group.ElementDic.Values)
                    {
                        if (item is Path)
                        {
                            list.Add((Path)item);
                        }
                    }
                }
                return Path.MergeAllPath(list.ToArray());
            }

            Dictionary<string, Element> _elementDic = null;
            public Dictionary<string, Element> ElementDic
            {
                get
                {
                    if (_elementDic == null)
                    {
                        _elementDic = new Dictionary<string, Element>();
                    }
                    return _elementDic;
                }
                set { _elementDic = value; }
            }

            public override Element CopyToGroupElement(Element el)
            {
                throw new NotImplementedException();
            }
            public List<int[]> MergeAllPath()
            {
                return MergeAllGroupPath(new Group[] { this });
            }
        }
        #endregion

        public const string DEFAULT_GROUP_KEY = "DEFAULT";

        public int DesignX { get; set; }
        public int DesignY { get; set; }

        public uint BeginTime { get; set; }
        public uint? ExecTime { get; set; }
        public uint EndTime { get; set; }

        Dictionary<string, Group> _groupDic = null;
        public Dictionary<string, Group> GroupDic
        {
            get
            {
                if (_groupDic == null)
                {
                    _groupDic = new Dictionary<string, Group> { { DEFAULT_GROUP_KEY, new Group { Name = DEFAULT_GROUP_KEY } } };
                }
                return _groupDic;
            }
            set { _groupDic = value; }
        }

        public void UpdateAllGroup()
        {
            foreach (var groupKey in GroupDic.Keys)
            {
                var group = GetGroup(groupKey);
                var elDic = GetGroup().ElementDic;
                var newDic = new Dictionary<string, Element>();
                foreach (var elKey in elDic.Keys)
                {
                    if (!group.ElementDic.ContainsKey(elKey))
                    {
                        newDic.Add(elKey, elDic[elKey].Clone());
                    }
                    else
                    {
                        newDic.Add(elKey, elDic[elKey].CopyToGroupElement(group.ElementDic[elKey]));
                    }
                }
                group.ElementDic = newDic;
            }
        }
        public Group[] FindGroup(params string[] names)
        {
            var nameList = new List<string>();
            foreach (var name in names)
            {
                nameList.Add(name.ToLower());
            }
            var list = new List<Group>();
            foreach (var item in GroupDic.Values)
            {
                if (nameList.Contains(item.Name.ToLower()))
                {
                    list.Add(item);
                }
            }
            return list.ToArray();
        }
        public Group GetGroup(string groupKey = DEFAULT_GROUP_KEY)
        {
            if (_groupDic.ContainsKey(groupKey))
            {
                return _groupDic[groupKey];
            }
            return _groupDic[DEFAULT_GROUP_KEY];
        }
        public uint GetGroupEndTime(RPack rpack, StartType startType = StartType.ZERO, string groupKey = DEFAULT_GROUP_KEY)
        {
            uint max = 0;
            var group = GetGroup(groupKey);
            foreach (var item in group.ElementDic.Values)
            {
                Timeline tl = null;
                if (item is Timeline && (tl = (Timeline)item).StartType == startType)
                {
                    max = Math.Max(max, tl.GetEndTime(rpack));
                }
            }
            return max;
        }
        public string[] GetGroupAllElementKeys(string groupKey = DEFAULT_GROUP_KEY, params Type[] types)
        {
            var group = GetGroup(groupKey);
            var typeList = new List<string>();
            var groupDic = new Dictionary<string, List<string>>();
            if (types.Length == 0)
            {
                typeList.Add(typeof(Path).FullName);
                typeList.Add(typeof(Label).FullName);
                typeList.Add(typeof(KeyFrameAnim).FullName);
                typeList.Add(typeof(Image).FullName);
            }
            foreach (var type in types)
            {
                typeList.Add(type.FullName);
            }
            foreach (var name in typeList)
            {
                if (!groupDic.ContainsKey(name))
                {
                    groupDic.Add(name, new List<string>());
                }
            }
            foreach (var key in group.ElementDic.Keys)
            {
                var item = group.ElementDic[key];
                var name = item.GetType().FullName;
                if (typeList.Contains(name))
                {
                    groupDic[name].Add(key);
                }
            }
            var list = new List<string>();
            foreach (var keys in groupDic.Values)
            {
                foreach (var key in keys)
                {
                    list.Add(key);
                }
            }
            return list.ToArray();
        }
        public Element GetGroupElement(string elKey, string groupKey = DEFAULT_GROUP_KEY)
        {
            var group = GetGroup(groupKey);
            if (group.ElementDic.ContainsKey(elKey))
            {
                return group.ElementDic[elKey];
            }
            return null;
        }
        public T GetGroupElement<T>(string elKey, string groupKey = DEFAULT_GROUP_KEY)
        {
            var el = GetGroupElement(elKey, groupKey);
            if (el is T)
            {
                return (T)(object)el;
            }
            return default(T);
        }
    }
    //#endregion

    [Serializable]
    public class RPack
    {
        public static RPack FromBytes(byte[] bytes)
        {
            try
            {
                return comm.BinarySerializer.Deserialize<RPack>(bytes);
            }
            catch (Exception ex)
            {
                Console.WriteLine("RPack FromBytes() err!" + ex.Message);
            }
            return null;
        }
        public static RPack FromFile(string file)
        {
            try
            {
                return FromBytes(File.ReadAllBytes(file));
            }
            catch (Exception ex)
            {
                Console.WriteLine("RPack FromFile(" + file + ") err! " + ex.Message);
            }
            return null;
        }

        public const string FILE_EXT_NAME = ".rpack";

        #region Resource
        [Serializable]
        public abstract class Resource
        {
            string _name = null;
            public string Name
            {
                get
                {
                    if (_name == null)
                    {
                        _name = "";
                    }
                    return _name;
                }
                set { _name = value; }
            }

            private string _tag = null;
            public string Tag
            {
                get
                {
                    if (_tag == null)
                    {
                        _tag = "";
                    }
                    return _tag;
                }
                set { _tag = value; }
            }
        }
        #endregion

        Dictionary<string, Resource> _resourceDic = null;
        public Dictionary<string, Resource> ResourceDic
        {
            get
            {
                if (_resourceDic == null)
                {
                    _resourceDic = new Dictionary<string, Resource>();
                }
                return _resourceDic;
            }
            set { _resourceDic = value; }
        }

        public string[] GetAllResourceKeys(params Type[] types)
        {
            var typeList = new List<string>();
            var groupDic = new Dictionary<string, List<string>>();
            if (types.Length == 0)
            {
                typeList.Add(typeof(RSprite).FullName);
                typeList.Add(typeof(RKeyFrameAnim).FullName);
                typeList.Add(typeof(RImage).FullName);
            }
            foreach (var type in types)
            {
                typeList.Add(type.FullName);
            }
            foreach (var name in typeList)
            {
                if (!groupDic.ContainsKey(name))
                {
                    groupDic.Add(name, new List<string>());
                }
            }
            foreach (var key in _resourceDic.Keys)
            {
                var item = _resourceDic[key];
                var name = item.GetType().FullName;
                if (typeList.Contains(name))
                {
                    groupDic[name].Add(key);
                }
            }
            var list = new List<string>();
            foreach (var keys in groupDic.Values)
            {
                foreach (var key in keys)
                {
                    list.Add(key);
                }
            }
            return list.ToArray();
        }
        public Resource GetResource(string resKey)
        {
            if (_resourceDic.ContainsKey(resKey))
            {
                return _resourceDic[resKey];
            }
            return null;
        }
        public T GetResource<T>(string resKey)
        {
            var res = GetResource(resKey);
            if (res is T)
            {
                return (T)(object)res;
            }
            return default(T);
        }
        public Resource[] FindResource(params string[] names)
        {
            var nameList = new List<string>();
            foreach (var name in names)
            {
                nameList.Add(name.ToLower());
            }
            var list = new List<Resource>();
            foreach (var item in _resourceDic.Values)
            {
                if (nameList.Contains(item.Name.ToLower()))
                {
                    list.Add(item);
                }
            }
            return list.ToArray();
        }
        public T[] FindResource<T>(string name)
        {
            var list = new List<T>();
            var res = FindResource(name);
            foreach (var item in res)
            {
                if (item is T)
                {
                    list.Add((T)(object)item);
                }
            }
            return list.ToArray();
        }
    }
}